/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package Negocio;

import Dominio.Arco;
import Dominio.Estado;
import Dominio.Nodo;
import Dominio.Vertice;
import Persistencia.DAOCallejero;
import Persistencia.DAOCoordenadas;
import java.sql.SQLException;
import java.util.LinkedList;
import java.util.TreeSet;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 *
 * @author beatriz
 */
public class Busqueda {

    private Nodo nodoInicial;
    private int origen;
    private int destino;
    private int tipoBusqueda;
    private int maxBares;
    private int maxDistancia;
    private int maxOcio;
    private DAOCallejero daoCallejero = null;
    private DAOCoordenadas daoCoordenadas = null;

    public Busqueda(int origen, int destino, int tipoBusqueda, int maxDistancia, int maxBares, int maxOcio) {
        this.origen = origen;
        this.destino = destino;
        this.tipoBusqueda = tipoBusqueda;
        Estado estadoInicial = new Estado(origen, 0, 0);
        nodoInicial = new Nodo(0, null, estadoInicial); // Se construye el nodo inicial
        this.maxDistancia = maxDistancia;
        this.maxBares = maxBares;
        this.maxOcio = maxOcio;
    }
    public Busqueda(){

    }

//    public Nodo anchura() {
//        LinkedList<Nodo> frontera = new LinkedList();
//        frontera.add(nodoInicial);
//        boolean seguir = true;
//        Nodo nodo = new Nodo();
//        while (!frontera.isEmpty() && seguir) {
//            nodo = frontera.getFirst();
//            frontera.removeFirst();
//            if (TestObjetivo(nodo)) {
//                seguir = false;
//            } else {
//                frontera.(puedoExpandir(nodo, (short) 0));//El cero indica que es búsqueda en anchura
//            }
//        }
//        return nodo;
//    }//Fin anchura
    public Nodo lanzarBusqueda(int cota) {
        TreeSet<Nodo> frontera = new TreeSet<Nodo>();
        Nodo nodoActual = null;
        Nodo nodoSolucion = null;
        int profundidad = 0;
        boolean puedoExpandir = true;

        frontera.add(nodoInicial);

        while (!frontera.isEmpty() && nodoSolucion == null) {
            nodoActual = frontera.first();
            frontera.remove(nodoActual);
            profundidad = nodoActual.getEstado().getProfundidad();
            puedoExpandir=true;

            if (testObjetivo(nodoActual)) {
                nodoSolucion = nodoActual;
            } else {
                //Profundidad || profundidad acotada || profundidad Iterativa
                if (tipoBusqueda == 2 || tipoBusqueda == 3 || tipoBusqueda == 4) {
                    if (profundidad > cota) {
                        puedoExpandir = false;
                    }
                }
                if (puedoExpandir) {
                    expandir(frontera, nodoActual, cota);
                }

            }

        }
        //BORRAME????????????????????????????????????????
        if (nodoSolucion == null && tipoBusqueda == 4) {
            cota++;
            nodoSolucion = this.lanzarBusqueda(cota);
        }

        ///fIN BORRAME??????????????
        return nodoSolucion;
    }

    private boolean testObjetivo(Nodo n) {
        if (n.getEstado().getIdNodo() == destino) {
            return true;
        } else {
            return false;
        }
    }

    private void expandir(TreeSet<Nodo> frontera, Nodo n, int maxProfundidad) {
        LinkedList<Arco> arcosCandidatos = new LinkedList<Arco>();
        Vertice vOrigen = null;
        Vertice vDestino = null;
        Arco a;
        Estado e;
        Nodo nodoSucesor = null;


        if (daoCallejero == null) {
            try {
                daoCallejero = new DAOCallejero();
            } catch (SQLException ex) {
                Logger.getLogger(Busqueda.class.getName()).log(Level.SEVERE, null, ex);
                System.out.print("DAOCallejero:Error al conectar con la base de datos");
            }
        }
        if (daoCoordenadas == null) {
            try {
                daoCoordenadas = new DAOCoordenadas();
            } catch (SQLException ex) {
                Logger.getLogger(Busqueda.class.getName()).log(Level.SEVERE, null, ex);
                System.out.print("DAOCoordenadas:Error al conectar con la base de datos");
            }
        }


        arcosCandidatos = daoCallejero.select("*", "destino = '" + n.getEstado().getIdNodo() + "' || origen = '" + n.getEstado().getIdNodo() + "'");

        while (!arcosCandidatos.isEmpty()) {
            a = arcosCandidatos.getFirst();
            arcosCandidatos.removeFirst();

            if (n.getEstado().getIdNodo() == a.getOrigen()) {
                e = new Estado(a.getDestino(), (n.getEstado().getProfundidad() + 1), calcularCoste(a));
            } else { //n.idNodo == a.getDestino()
                e = new Estado(a.getOrigen(), n.getEstado().getProfundidad() + 1, calcularCoste(a));
            }

            switch (tipoBusqueda) {
                case 1: //ANCHURA
                    nodoSucesor = new Nodo((float) n.getEstado().getProfundidad() + 1, n, e);
                    break;
                case 2: //PROFUNDIDAD
                case 3: //PROFUNDIDAD ACOTADA
                case 4: //PROFUNDIADD ITERATIVA
                    nodoSucesor = new Nodo((float) (maxProfundidad - (n.getEstado().getProfundidad() + 1)), n, e);
                    break;
                case 5: //COSTO UNIFORME
                    nodoSucesor = new Nodo((n.getCoste() + e.getCosto()), n, e);
                    break;
                case 6://A*
                    vOrigen = daoCoordenadas.select("*", "idNodo = '" + n.getEstado().getIdNodo() + "'");
                    vDestino = daoCoordenadas.select("*", "idNodo = '" + destino + "'");

                    nodoSucesor = new Nodo((n.getCoste() + distanciaEuclidea(vOrigen, vDestino) + e.getCosto()), n, e);
                    break;

                default:
                    System.out.println("Opcion imposible");
            }
            frontera.add(nodoSucesor);

        }

    }

    public void setDestino(int destino) {
        this.destino = destino;
    }

    public void setMaxBares(int maxBares) {
        this.maxBares = maxBares;
    }

    public void setMaxDistancia(int maxDistancia) {
        this.maxDistancia = maxDistancia;
    }

    public void setMaxOcio(int maxOcio) {
        this.maxOcio = maxOcio;
    }

    public void setOrigen(int origen) {
        this.origen = origen;
    }

    public void setTipoBusqueda(int tipoBusqueda) {
        this.tipoBusqueda = tipoBusqueda;
    }

    public void setNodoInicial(Nodo nodoInicial) {
        this.nodoInicial = nodoInicial;
    }

    private float calcularCoste(Arco a) {
        return (float) (((0.3 * a.getDistancia()) / maxDistancia) + ((0.3 * (maxBares - a.getBares())) / maxBares) + ((0.3 * (maxOcio - a.getOcio())) / maxOcio));
    }

    private int distanciaEuclidea(Vertice v, Vertice dest) {
        int distancia = 0;
        System.out.println(v.getIdNodo());

        double cuadradoX = Math.pow(dest.getLatitud() - v.getLatitud(), 2.0);
        double cuadradoY = Math.pow(dest.getLongitud() - v.getLongitud(), 2.0);
        distancia = (int) Math.sqrt(cuadradoX + cuadradoY);
        return distancia;
    }


}
